// (c) Copyright Damian Mehers (damian@mehers.com http://damianblog.com/)
// This source is subject to the Microsoft Public License.
// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
// All other rights reserved.
// GetPasswordHashWixCA.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"
#include <stdio.h>
#include <Wincrypt.h>
#include <time.h>
#include <windows.h>
#include <wincrypt.h>
#include <stdio.h>
#include <msi.h>
#include <msiquery.h>
#include <Wininet.h>

#pragma comment(linker, "/EXPORT:GetPasswordHash=_GetPasswordHash@4")

#define BUF_LEN 256

//#define LOG 1

#define HASHED_LEN  32


// Gets the username and password setup properties
UINT GetUsernamePassword(HWND parentWindow, MSIHANDLE hInstall, wchar_t* username, wchar_t* password, DWORD len) {
#ifdef LOG
	MessageBox(parentWindow, L"GetUsernamePassword called", L"GetPasswordHash", MB_OK);
#endif
    DWORD usernameSize = len;
    UINT rc = MsiGetProperty(hInstall, L"USERNAME", username, &usernameSize);
    if (rc != S_OK) 
    {
#ifdef LOG
        MessageBox(parentWindow, L"MsiGetProperty(USERNAME) failed.", L"Error", MB_OK);
#endif
        return ERROR_INSTALL_FAILURE;
    }

    DWORD passwordSize = len;
    rc = MsiGetProperty(hInstall, L"PASSWORD", password, &passwordSize);
    if (rc != S_OK) 
    {
#ifdef LOG
        MessageBox(parentWindow, L"MsiGetProperty(PASSWORD) failed.", L"Error", MB_OK);
#endif
        return ERROR_INSTALL_FAILURE;
    }

	return S_OK;

}

 bool CheckPassword(wchar_t* username, wchar_t* password) {
	// http://ws.audioscrobbler.com//ass/pwcheck.php?time=[TIMESTAMP]&username=[USERNAME]&auth=[AUTH1]&auth2=[AUTH2]&defaultplayer=[PLAYER]
	return false;
}

 // Uses crypto API to get the MD5 password hash
 HRESULT GetHash(HWND parentWindow, wchar_t* password, wchar_t* hash) {
    unsigned long hHash;

	HRESULT rc = S_OK;
	HCRYPTPROV hCryptProv;
	bool ok = CryptAcquireContext(&hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET) != 0;
	if(!ok) {
		rc = GetLastError();
        if(rc == NTE_EXISTS){
			ok = CryptAcquireContext(&hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, 0) != 0;
			if(!ok) return GetLastError();
		} else {
			return rc;
		}
    }

    CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hHash);

	unsigned char uc_buf[BUF_LEN];

	int len = wcslen(password);

	for(int i = 0; i < len; i++) {
		uc_buf[i] = password[i];
	}
	
	uc_buf[len] = '\0';
#ifdef LOG
        MessageBox(parentWindow, password, L"GetPasswordHash password", MB_OK);
        MessageBoxA(parentWindow, (char*)uc_buf, "GetPasswordHash password", MB_OK);
#endif

    CryptHashData(hHash, uc_buf, len, 0);

    DWORD dwCount = 16;
    unsigned char digest[16];
    CryptGetHashParam(hHash, HP_HASHVAL, digest, &dwCount, 0);
    if(hHash) CryptDestroyHash(hHash);
    hHash = 0;

	for(int i = 0; i < 16; i++) {
		wsprintf(&hash[i * 2], L"%02x", digest[i]);
	}
	hash[HASHED_LEN] = '\0';

	CryptReleaseContext(hCryptProv, 0);
	return S_OK;
 }

 void SetOKFlag(MSIHANDLE hInstall, bool ok) {
	 MsiSetProperty(hInstall, L"USERNAMEPASSWORDOK", ok ? L"1" : L"0");

 }


extern "C" UINT __stdcall GetPasswordHash (MSIHANDLE hInstall) {
#ifdef LOG
	MessageBox(0, L"Called", L"GetPasswordHash", MB_OK);
#endif
    HWND parentWindow = GetForegroundWindow();


	// Get the username and password
	wchar_t username[BUF_LEN];
	wchar_t password[BUF_LEN];

    UINT rc = GetUsernamePassword(parentWindow, hInstall, username, password, sizeof(username));
	if (rc != S_OK)  {
#ifdef LOG
	MessageBox(parentWindow, L"Returning ERROR_INSTALL_FAILURE", L"GetPasswordHash", MB_OK);
#endif
		return ERROR_INSTALL_FAILURE;
	}

	// Check that both the username and password are not blank
	if(wcslen(username) == 0) {
		MessageBox(parentWindow, L"Please enter a username", L"MceFM Setup", MB_OK);
		SetOKFlag(hInstall, false);
		return S_OK;
	}

	if(wcslen(password) == 0) {
		MessageBox(parentWindow, L"Please enter a password", L"MceFM Setup", MB_OK);
		SetOKFlag(hInstall, false);
		return S_OK;
	}

	// Create the hash of the password
	wchar_t result[HASHED_LEN + 1];
	rc = GetHash(parentWindow, password, result);

	if(rc != S_OK) {
		return rc;
	}


#ifdef LOG
	MessageBox(parentWindow, result, L"Base64 password hash", MB_OK);
#endif


	// TODO: Call the Last.fm API to check the password

	MsiSetProperty(hInstall, L"PASSWORDHASH", result);
	SetOKFlag(hInstall, true);


	return S_OK; 
}
